home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Magnum One
/
Magnum One (Mid-American Digital) (Disc Manufacturing).iso
/
d7
/
commdrvr.arc
/
TIRS232.ASM
< prev
next >
Wrap
Assembly Source File
|
1988-04-11
|
29KB
|
780 lines
page 60,132
;***********************************************************
;** **
;** Device Driver for RS232 communications **
;** TI PC Version **
;** Copyright (C) Texas Instruments 1986 **
;** Author: Greg Haley **
;** **
;** THIS SOURCE CODE MAY BE DISTRIBUTED AND MODIFIED **
;** ONLY IF THE ORIGINAL COPYRIGHT AND AUTHOR CREDITS **
;** REMAIN INTACT. **
;** **
;** Project Start Date: 11/20/86 **
;** **
;** RE: 12/29/86 by Greg Haley **
;** Added send_xon routine. **
;** Added TI internal modem support. **
;** Added 19200 baud. **
;** **
;***********************************************************
name tirs232
title Device Driver for TI PC Communications
code segment byte
assume cs:code,ds:nothing,es:nothing
include rs232.inc
page
;***********************************************************
;** Keyboard Routines **
;***********************************************************
keyboard equ 4ah ; TI PC keyboard INT
altah db 0 ; Storage for high byte of key input
k_ready:
mov al,cs:altah ; Get 2nd half of F-key?
or al,al
jnz k_rdy_xit ; Yes, skip to end
mov ah,1 ; Is a char waiting?
int keyboard
jz k_rdy_xit ; No, exit
or ax,ax ; If it was a ^C, remove it
jnz k_rdy_xit
xor ah,ah
int keyboard
jmp short k_ready ; And try again
k_rdy_xit:
ret
k_read:
xor ax,ax ; Clear AX
xchg al,cs:altah ; Get 2nd half of F-key?
or al,al
jnz k_r_xit ; Yes, skip to end
k_r_1:
xor ah,ah
int keyboard
or ax,ax ; Was it a ^C?
jz k_r_1 ; Yes, get another
or al,al ; Is it a F-key?
jnz k_r_xit ; No, exit
mov cs:altah,ah ; Yes, save 2nd half
k_r_xit:
ret
page
;***********************************************************
;** Communications Routines **
;***********************************************************
recv_size equ 800h ; Receive buffer size
; Must be one of these values:
; 0004h = 4 bytes
; 0008h = 8 bytes
; 0010h = 16 bytes
; 0020h = 32 bytes
; 0040h = 64 bytes
; 0080h = 128 bytes
; 0100h = 256 bytes
; 0200h = 512 bytes
; 0400h = 1024 bytes
; 0800h = 2048 bytes
; 1000h = 4096 bytes
; 2000h = 8192 bytes
; 4000h = 16384 bytes
; 8000h = 32768 bytes
recv_limit equ not recv_size ; Receive buffer limit mask
busy_len equ recv_size *3 /4 ; Go busy at 3/4 buf length
not_busy_len equ recv_size /2 ; Not busy at 1/2 buf length
int_controller equ 19h ; 8259A Interrupt Controller port
int_ack equ 18h ; 8259A Interrupt Acknowledge port
EOI equ 00100000b ; Non-specific EOI
enable_ints equ 01000000b ; Enable interrupts
disable_ints equ 11000000b ; Disable interrupts
dtr_bit equ 1 ; DTR bit in uart control register
rts_bit equ 2 ; DTR bit in uart control register
dcd_bit equ 1 ; DCD bit in modem status register
cts_bit equ 2 ; CTS bit in modem status register
dsr_bit equ 4 ; DSR bit in modem status register
scf_bit equ 8 ; SCF bit in modem status register
ri_bit equ 16 ; RI bit in modem status register
xon equ 11h ; xmit on busy char
xoff equ 13h ; xmit off busy char
page
;***********************************************************
;** Variables **
;***********************************************************
old_ss dw 0 ; Old SS reg
old_sp dw 0 ; Old SP reg
db 80 dup (?) ; Stack
i_stack label byte
rq_head dw 0 ; Receive Queue start
rq_tail dw 0 ; Receive Queue stop
rq_len dw 0 ; Current receive Queue length
rqueue db recv_size dup (?) ; Receive queue
oldseg dw 0 ; Old segment for int vector
oldoff dw 0 ; Old offset for int vector
busy_hand db 0 ; busy handling type
r_busy db 0 ; recv busy flag
t_busy db 0 ; xmit busy flag
xmit_busy db 0 ; xmit busy flag (for ints)
m_stat db 0 ; Current modem status
l_stat db 0 ; Current line status
dcw dw 1100000001010000b ; default 2400, 8, N, 1
parity_on db 0 ; Parity flag
ti_modem db 0 ; Flag for TI internal modem
opn_tbl:
db 0 ; Needs leading 0 for sync
db 9 ; select reg 9
db 01001001b ; Reset chan B, MIE, & VIS
db 12 ; select reg 12
baudl db 30 ; Low byte of baud rate (2400)
db 13 ; select reg 13
baudh db 0 ; High byte of baud rate
db 2 ; select reg 2
db 0 ; int vector on
db 11 ; select reg 11
db 01010010b ; brate generator
db 14 ; select reg 14
db 00000011b ; enable brate gen.
db 4 ; select reg 4
par_stp db 01000100b ; No parity, 1 stop bit
db 3 ; select reg 3
recv_b db 11000001b ; recv enable, 8 bits
db 5 ; select reg 5
xmit_b db 11101010b ; xmit enable, DTR, 8 bits
db 10 ; select reg 10
db 0 ; NRZ, flags
db 15 ; select reg 15
db 00101000b ; CTS, DCD int enable
db 1 ; select reg 1
db 00010111b ; int enable
opn_size equ $-opn_tbl
opn2_tbl:
db 0 ; Sync byte
db 15 ; select reg 15
db 00101000b ; RI & DSR int enable
db 1 ; select reg 1
db 00000001b ; external int enable
opn2_size equ $-opn2_tbl
int_tbl dw 0 ; Interrupt branch table
dw mod_stat
dw 0
dw 0
dw xmit_mt
dw mod_stat
dw rec_full
dw lin_stat
; table for baud rate constants
baud_tbl dw 696 ; 110
dw 510 ; 150
dw 254 ; 300
dw 126 ; 600
dw 62 ; 1200
dw 30 ; 2400
dw 14 ; 4800
dw 6 ; 9600
; table for port number init tables
p_table dw port1_tbl
dw port2_tbl
dw port3_tbl
dw port4_tbl
port_n dw port1_tbl ; default port is 1
; port 1 init table
port1_tbl dw 40h ; Interrupt vector for irq
db 11111110b ; Mask to enable irq
db 00000001b ; Mask to unable irq
dw 0e7h ; Receive buffer port
dw 0e7h ; Transmit buffer port
dw 0e6h ; 8530 UART Control port
dw 0e6h ; 8530 UART Status port
dw 0e4h ; 8530 Modem Status port
dw 0e6h ; 8530 interrupt enable register
dw 0e0h ; 8530 interrupt ack register
p_tbl_size equ $-port1_tbl
; port 2 init table
port2_tbl dw 41h ; Interrupt vector for irq
db 11111101b ; Mask to enable irq
db 00000010b ; Mask to unable irq
dw 0efh ; Receive buffer port
dw 0efh ; Transmit buffer port
dw 0eeh ; 8530 UART Control port
dw 0eeh ; 8530 UART Status port
dw 0ech ; 8530 Modem Status port
dw 0eeh ; 8530 interrupt enable register
dw 0e8h ; 8530 interrupt ack register
; port 3 init table
port3_tbl dw 42h ; Interrupt vector for irq
db 11111011b ; Mask to enable irq
db 00000100b ; Mask to unable irq
dw 0f7h ; Receive buffer port
dw 0f7h ; Transmit buffer port
dw 0f6h ; 8530 UART Control port
dw 0f6h ; 8530 UART Status port
dw 0f4h ; 8530 Modem Status port
dw 0f6h ; 8530 interrupt enable register
dw 0f0h ; 8530 interrupt ack register
; port 4 init table
port4_tbl dw 44h ; Interrupt vector for irq
db 11101111b ; Mask to enable irq
db 00010000b ; Mask to unable irq
dw 0ffh ; Receive buffer port
dw 0ffh ; Transmit buffer port
dw 0feh ; 8530 UART Control port
dw 0feh ; 8530 UART Status port
dw 0fch ; 8530 Modem Status port
dw 0feh ; 8530 interrupt enable register
dw 0f8h ; 8530 interrupt ack register
; defaults for port 1
port_tbl label word
comm1_vector dw 40h ; Interrupt vector for irq
irq_enab_mask db 11111110b ; Mask to enable irq
irq_unab_mask db 00000001b ; Mask to unable irq
recv_buffer dw 0e7h ; Receive buffer port
send_buffer dw 0e7h ; Transmit buffer port
uart_control dw 0e6h ; 8530 UART Control port
uart_status dw 0e6h ; 8530 UART Status port
modem_status dw 0e4h ; 8530 Modem Status port
int_enable dw 0e6h ; 8530 interrupt enable register
ack_int dw 0e0h ; 8530 interrupt ack register
page
;***********************************************************
;** Subroutine to set up comm chip per the DCW **
;** DCW is in AX **
;***********************************************************
set_dcw:
; set parity type
mov bl,al ; get low byte in BL
and bl,00000011b ; mask unused bits
and byte ptr par_stp,11111100b ; reset parity
or byte ptr par_stp,bl ; change parity
and bl,00000001b ; change parity flag
mov byte ptr parity_on,bl ;
; set num stop bits
mov bl,al ; get low byte in BL
and bl,00001000b ; mask unused bits
or bl,00000100b ; 1.5 stop bits not used
and byte ptr par_stp,11110011b ; reset stop bits
or byte ptr par_stp,bl ; change stop bits
; set baud rate
mov bl,al ; get low byte in BL
and bl,01110000b ; mask unused bits
mov cl,3 ; shift to make word ptr
shr bl,cl
mov si,offset baud_tbl ; point to baud table
xor bh,bh ; make BX a byte ptr
add si,bx ; SI now points to baud rate const
mov dx,word ptr [si] ; get baud rate in DX
mov byte ptr baudl,dl ; save low byte of baud rate
mov byte ptr baudh,dh ; save high byte of baud rate
; set busy type
mov bl,ah
and bl,00000011b ; mask unwanted bits
mov byte ptr busy_hand,bl ; store it
; set port number
mov bl,ah ; get high byte in BL
and bl,00011000b ; mask unused bits
shr bl,1 ; shift to make word ptr
shr bl,1
mov si,offset p_table ; point to port table
xor bh,bh ; make BX a byte ptr
add si,bx ; SI now points to baud rate const
mov dx,word ptr [si] ; get port adrs in DX
mov word ptr port_n,dx ; save port number adrs
; set data bits
mov bl,ah ; get high byte in BL
and bl,01000000b ; mask unused bits
or bl,00100000b ; make it at least 7 bits
and byte ptr xmit_b,10011111b ; reset xmit data size
or byte ptr xmit_b,bl ; set xmit data size
shl bl,1 ; adjust for recv data size
and byte ptr recv_b,00111111b ; reset recv data size
or byte ptr recv_b,bl ; set recv data size
; Check for internal modem
mov ti_modem,al
and ti_modem,00000100b ; TI internal modem if not 0
ret
page
;***********************************************************
;** Subroutine to set up interrupt vector **
;** and initialize the 8530 comm chip **
;***********************************************************
init_comm:
push ds
push cs
pop ds
; get correct port parameters
push es
push cs
pop es
mov si,word ptr port_n ; get port table adrs
mov di,offset port_tbl ; DI points to table to use
mov cx,p_tbl_size ; CX has table length
repz movsb ; move it
pop es
; Save old int vector for irq
mov di,comm1_vector
call get_vector
mov word ptr oldseg,bx
mov word ptr oldoff,dx
; Set up int vector for irq
push cs ; Make BX = CS
pop bx
mov dx,offset isr
mov di,comm1_vector
call set_vector
; Enable irq from 8259A
cli
in al,int_controller
jmp $+2 ; delay
and al,irq_enab_mask
out int_controller,al
; Read receive buffer register
mov dx,recv_buffer
in al,dx
; Read modem status register
call mod_stat
; Read UART status register
call lin_stat
; open 8530
mov si,offset opn_tbl
mov cx,opn_size
mov dx,uart_control
init_1:
lodsb
out dx,al
jmp $+2 ; delay
loop init_1
mov si,offset opn2_tbl
mov cx,opn2_size
mov dx,modem_status
init_2:
lodsb
out dx,al
jmp $+2 ; delay
loop init_2
pop ds
sti
; Reset TI internal modem if present
cmp cs:byte ptr ti_modem,0 ; TI internal modem?
jz no_ti_modem ; No, exit
mov dx,modem_status ; Yes, set /RCNTL
mov al,5
out dx,al
mov al,cs:xmit_b
or al,2
out dx,al
wait_4_ack:
mov dx,uart_status ; Check for ACK
mov al,10h
out dx,al
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx
test al,10h ; Got an ACK?
jz wait_4_ack ; No, keep looping
no_ti_modem:
ret
page
;***********************************************************
;** Subroutine to restore interrupt vector **
;** and reset the 8530 comm chip **
;***********************************************************
de_init:
cli
; Disable interrupts on 8530 and drop DTR, RTS
mov dx,cs: word ptr int_enable
mov al,9 ; select reg 9
out dx,al
mov al,disable_ints
jmp $+2 ; delay
out dx,al
jmp $+2 ; delay
jmp $+2 ; delay
; Disable irq
in al,int_controller
or al,cs:byte ptr irq_unab_mask
jmp $+2 ; delay
jmp $+2 ; delay
out int_controller,al
; It's probably not a good idea to restore the vector at close in
; this case, but here's the code to do it:
;
; Restore int vector for irq
; mov bx,cs:word ptr oldseg
; mov dx,cs:word ptr oldoff
; mov di,comm1_vector
; call set_vector
sti
ret
page
;***********************************************************
;** Interrupt Service Routine **
;***********************************************************
isr:
cli
cld
; Set up new stack
mov cs:word ptr old_sp,sp
mov cs:word ptr old_ss,ss
mov sp,cs
mov ss,sp
mov sp,offset i_stack
push ax
push bx
push cx
push dx
push ds
push cs
pop ds
; Verify int came from 8530
mov dx,uart_status
mov al,3 ; reg 3
out dx,al ; select reg 3
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx ; read reg 3
or al,al ; Interrupt pending?
jz isr_exit ; No, exit
; Acknowledge int
mov dx,ack_int ; get int ack reg
xor al,al ; 1st write a 0
out dx,al
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx ; get int type
; Branch to correct routine
cbw ; make int type a word
mov bx,offset int_tbl ; point to int table
add bx,ax ; add int type
sti
call cs:word ptr [bx] ; go do subroutine
cli
; Reset highest int under service by 8530
mov al,00111000b ; reset highest int code
mov dx,int_enable ; select port
out dx,al ; reset the int
jmp $+2 ; delay
isr_exit:
; Tell 8259A we're done
mov al,EOI
out int_ack,al
pop ds
pop dx
pop cx
pop bx
pop ax
; restore stack
mov ss,cs:word ptr old_ss
mov sp,cs:word ptr old_sp
sti
iret
page
;***********************************************************
;** Subroutine to read the modem status **
;***********************************************************
mod_stat:
mov dx,modem_status ; Read modem status (CHB)
xor al,al ; reg 0
out dx,al ; Select reg 0
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx ; read reg 0
mov cl,5 ; move bits to AH
shl ax,cl
xor al,al ; Read reg 0
mov dx,modem_status ; Read modem status (CHB)
inc dx ; adjust to channel A
inc dx
out dx,al ; Select reg 0
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx ; into reg AL
shl al,1 ; adjust to match ACS lib
shl al,1
shl ax,1
shl al,1
shl ax,1
and ah,00011111b ; mask unwanted bits
; AH now contains the line signals
; ---------------------------------
; | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
; ---------------------------------
; | | | | RI|SCF|DSR|CTS|DCD|
; ---------------------------------
; 0=down, 1=up
mov cs:byte ptr m_stat,ah ; Update current modem status
; Check for busy handling
shr ah,1 ; DSR in bit 0
shr ah,1
cmp cs:byte ptr busy_hand,2 ; DSR busy handling?
jne chk_4scf ; No, skip
mov al,ah ; save AH
jmp set_tbusy ; go set or reset busy
chk_4scf:
cmp cs:byte ptr busy_hand,1 ; SCF busy handling?
jne rst_sts ; No, skip
shr ah,1
mov al,ah ; save AH
set_tbusy:
and al,1 ; up = busy on
; not al ; down = busy on
mov cs:byte ptr t_busy,al ; set busy
; reset ext stat int
rst_sts:
mov al,10h ; reset ext sts code
mov dx,modem_status ; reset chan B
out dx,al
jmp $+2 ; delay
mov dx,uart_control ; reset chan A
out dx,al
ret
page
;***********************************************************
;** Subroutine to send the next char in queue **
;***********************************************************
xmit_mt:
mov cs:byte ptr xmit_busy,0 ; We're not busy
; reset xmit int
mov al,00101000b ; code
mov dx,uart_control
out dx,al
ret
page
;***********************************************************
;** Subroutine to receive a char and queue it **
;***********************************************************
rec_full:
mov dx,recv_buffer ; Get char
in al,dx
cmp cs:byte ptr parity_on,0 ; parity?
jz no_par ; No, don't mask off parity bit
and al,7fh ; Yes, mask off parity bit
no_par:
; check for XON-XOFF busy char
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne queue_char ; No, skip busy handling
cmp al,xoff ; Need to set busy?
jne chk_r_xon ; No, skip
mov cs:byte ptr t_busy,1 ; set busy
jmp xit_rec_full ; We're done
chk_r_xon:
cmp al,xon ; Need to reset busy?
jne queue_char ; No, skip busy handling
mov cs:byte ptr t_busy,0 ; reset busy
jmp xit_rec_full ; We're done
queue_char:
; check for buffer overflow
cmp cs:word ptr rq_len,recv_limit ; buffer full?
jb buf_full1 ; No, skip
inc cs:word ptr rq_tail ; Yes, lose 1 char
and cs:word ptr rq_tail,recv_limit
dec cs:word ptr rq_len ; Adjust queue length
buf_full1:
mov bx,offset rqueue ; Queue char
mov dx,cs:word ptr rq_head
add bx,dx
mov cs:byte ptr [bx],al
inc dx
and dx,recv_limit ; wrap if >= receive size
mov cs:word ptr rq_head,dx
inc cs:word ptr rq_len ; Adjust queue length
; set busy if needed
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne set_rb_done ; No, skip
cmp cs:word ptr rq_len,busy_len ; Need to set busy?
jb set_rb_done ; No, skip
mov cs:byte ptr r_busy,1 ; set busy flag
mov dx,cs:word ptr send_buffer ; send XOFF char
mov al,xoff
out dx,al
set_rb_done:
xit_rec_full:
ret
page
;***********************************************************
;** Subroutine to send an xon char if needed **
;***********************************************************
send_xon:
cmp cs:byte ptr busy_hand,3 ; XON-XOFF busy handling?
jne xit_send_xon ; No, skip
mov dx,cs:word ptr send_buffer ; send XON char
mov al,xon
out dx,al
xit_send_xon:
ret
page
;***********************************************************
;** Subroutine to read the line status **
;***********************************************************
lin_stat:
mov dx,uart_status
mov al,1 ; select reg 1
out dx,al
jmp $+2 ; delay
jmp $+2 ; delay
in al,dx ; read status
mov cs:byte ptr l_stat,al ; Update current line status
; reset error
mov al,00110000b ; reset lin stat error code
out dx,al
ret
page
;***********************************************************
;** Subroutine to get an interrupt vector **
;** **
;** di = vector number **
;** **
;** Return: **
;** bx = segment **
;** dx = offset **
;***********************************************************
get_vector:
push es
xor ax,ax
mov es,ax
shl di,1
shl di,1
mov dx,es:word ptr[di]
mov bx,es:word ptr[di+2]
pop es
ret
page
;***********************************************************
;** Subroutine to set an interrupt vector **
;** **
;** di = vector number **
;** bx = segment **
;** dx = offset **
;***********************************************************
set_vector:
push es
xor ax,ax
mov es,ax
shl di,1
shl di,1
mov es:word ptr[di],dx
mov es:word ptr[di+2],bx
pop es
ret
page
;***********************************************************
;** Everything past here is truncated after install **
;***********************************************************
init proc near
lds bx,cs:[ptrsav]
mov word ptr [bx].trans,offset init ;set break address
mov [bx].trans+2,cs
print_init:
push cs
pop ds
mov dx,offset init_msg
mov ah,9
int 21h
jmp exit
init endp
init_msg:
db cr,lf,'TI PC Communications Driver v2.51'
db ' Copyright (C) Texas Instruments 1986'
db cr,lf,'Written by '
db 'Greg Haley'
db cr,lf,cr,lf,'$'
code ends
end